
// 外卖阶梯配送
package seller

import (
    "time"
	"errors"
	"strings"
	"strconv"
	"encoding/json"
	"dhfshop/models"
	"dhfshop/models/misc"
	"dhfshop/utils/math"
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
)

type FreightRule struct {
    FreightRuleId int64          `orm:"column(freight_rule_id);pk;auto" json:"freight_rule_id" form:"freight_rule_id"`
	PartnerId     int64          `orm:"column(partner_id)" json:"partner_id" form:"-"`
	StoreId       int64          `orm:"column(store_id)" json:"store_id" form:"store_id"`
	BaseFee       float32        `orm:"column(base_fee);digits(12);decimals(2)" json:"base_fee" form:"base_fee"`            
	DistanceFees  []*DistanceFee `orm:"reverse(many)" json:"distance_fees,omitempty"`
	WeightFees    []*WeightFee   `orm:"reverse(many)" json:"weight_fees,omitempty"`
	PeriodFees    []*PeriodFee   `orm:"reverse(many)" json:"period_fees,omitempty"`
	DistanceJson  string         `orm:"-" json:"-" form:"distance_fees"`                       
	WeightJson    string         `orm:"-" json:"-" form:"weight_fees"`                       
	PeriodJson    string         `orm:"-" json:"-" form:"period_fees"`                       
}
//距离额外配送费
type DistanceFee struct {
	DistanceFeeId int64        `orm:"column(distance_fee_id);pk;auto" json:"distance_fee_id"`
	FreightRule   *FreightRule `orm:"column(freight_rule_id);rel(fk);on_delete(cascade)" json:"-"`
	No            int          `orm:"column(no)" json:"no"`
	Lower         float32      `orm:"column(lower);digits(12);decimals(2)" json:"lower"`
	Upper         float32      `orm:"column(upper);digits(12);decimals(2)" json:"upper"`
	Unit          float32      `orm:"column(unit);digits(12);decimals(2)" json:"unit"`
	UnitFee       float32      `orm:"column(unit_fee);digits(12);decimals(2)" json:"unit_fee"`
}
//重量额外配送费
type WeightFee struct {
	WeightFeeId int64        `orm:"column(weight_fee_id);pk;auto" json:"weight_fee_id"`
	FreightRule *FreightRule `orm:"column(freight_rule_id);rel(fk);on_delete(cascade)" json:"-"` 
	No          int          `orm:"column(no)" json:"no"`
	Lower       float32      `orm:"column(lower);digits(12);decimals(2)" json:"lower"`
	Upper       float32      `orm:"column(upper);digits(12);decimals(2)" json:"upper"`
	Unit        float32      `orm:"column(unit);digits(12);decimals(2)" json:"unit"`
	UnitFee     float32      `orm:"column(unit_fee);digits(12);decimals(2)" json:"unit_fee"`
}
//时段额外配送费
type PeriodFee struct {
	PeriodFeeId int64        `orm:"column(period_fee_id);pk;auto" json:"period_fee_id"`
	FreightRule *FreightRule `orm:"column(freight_rule_id);rel(fk);on_delete(cascade)" json:"-"`
	No          int          `orm:"column(no)" json:"no"`
	Start       string       `orm:"column(start)" json:"start"`
	End         string       `orm:"column(end)" json:"end"`
	UnitFee     float32      `orm:"column(unit_fee);digits(12);decimals(2)" json:"unit_fee"`
}

func init() {
    orm.RegisterModel(new(FreightRule),new(DistanceFee),new(WeightFee),new(PeriodFee))   
}
/*
func (u *DistanceFee) TableUnique() [][]string {
	return [][]string{
		[]string{"FreightRule", "No"},
	}
}
func (u *WeightFee) TableUnique() [][]string {
	return [][]string{
		[]string{"FreightRule", "No"},
	}
}
func (u *PeriodFee) TableUnique() [][]string {
	return [][]string{
		[]string{"FreightRule", "No"},
	}
}
*/

func AddFreightRule(m *FreightRule) (id int64, err error) {
	o := models.GetDefaultDB()
	id, err = o.Insert(m)
	return
}

func AddMulDistanceFees(list []*DistanceFee) (err error) {
	size := len(list)
	o := models.GetDefaultDB()
	if size > 1 {
	    _, err = o.InsertMulti(size, list) 
	}else{
	   _, err = o.Insert(list[0])
	}
	return
}

func AddMulWeightFees(list []*WeightFee) (err error) {
	size := len(list)
	o := models.GetDefaultDB()
	if size > 1 {
	    _, err = o.InsertMulti(size, list) 
	}else{
	   _, err = o.Insert(list[0])
	}
	return
}

func AddMulPeriodFees(list []*PeriodFee) (err error) {
	size := len(list)
	o := models.GetDefaultDB()
	if size > 1 {
	    _, err = o.InsertMulti(size, list) 
	}else{
	   _, err = o.Insert(list[0])
	}
	return
}

func GetFreightRuleById(id int64) (v *FreightRule, err error) {
	o := models.GetDefaultDB()
	v = &FreightRule{FreightRuleId: id}
	if err = o.Read(v); err == nil {
		v.DistanceFees, _= GetDistanceFeesByLower(id)
		v.WeightFees, _= GetWeightFeesByLower(id)
		v.PeriodFees, _= GetPeriodFeesByNo(id)
		return v, nil
	}
	return nil, err
}

func GetDistanceFeesByLower(freightRuleId int64) (lists []*DistanceFee, err error) {
	o := models.GetDefaultDB()
	_, err = o.QueryTable(new(DistanceFee)).Filter("freight_rule_id", freightRuleId).OrderBy("lower").All(&lists)
	return
}

func GetWeightFeesByLower(freightRuleId int64) (lists []*WeightFee, err error) {
	o := models.GetDefaultDB()
	_, err = o.QueryTable(new(WeightFee)).Filter("freight_rule_id", freightRuleId).OrderBy("lower").All(&lists)
	return
}
func GetPeriodFeesByNo(freightRuleId int64) (lists []*PeriodFee, err error) {
	o := models.GetDefaultDB()
	_, err = o.QueryTable(new(PeriodFee)).Filter("freight_rule_id", freightRuleId).OrderBy("no").All(&lists)
	return
}

func GetAllFreightRules(query,orderBy map[string]string,fields []string,offset,limit int)([]*FreightRule, int64,error) {
	o := models.GetDefaultDB()
	qs := o.QueryTable(new(FreightRule))
	for k, v := range query {
		k = strings.Replace(k, ".", "__", -1)
		qs = qs.Filter(k, v)
	}
	cnt, err := qs.Count()
	// order by:
	var sortFields []string
	for k, v := range orderBy {
		orderby := ""
		if v == "desc" {
			orderby = "-" + k
		} else if v == "asc" {
			orderby = k
		} else {
			return nil, 0, errors.New("Error: Invalid order. Must be either [asc|desc]")
		}
		sortFields = append(sortFields, orderby)
	}
	qs = qs.OrderBy(sortFields...)
	var lists []*FreightRule
	if _, err := qs.Limit(limit, offset).All(&lists, fields...); err == nil {
	    for _, temp := range lists {
			o.LoadRelated(temp, "DistanceFees")
			for _, mode := range temp.DistanceFees { 		
			    mode.FreightRule = nil
			}
			o.LoadRelated(temp, "WeightFees")
			for _, mode := range temp.WeightFees { 	
			    mode.FreightRule = nil
			}
			o.LoadRelated(temp, "PeriodFees")
			for _, mode := range temp.PeriodFees { 		
			    mode.FreightRule = nil
			}
	    }
		return lists, cnt, nil
	}
	return nil, 0, err
}

func UpdateFreightRuleById(m *FreightRule, k ...string) (err error) {
	o := models.GetDefaultDB()
	v := FreightRule{FreightRuleId: m.FreightRuleId}
	if err = o.Read(&v); err != nil {
		return err
	}else{
	    if _, err = o.Update(m, k...); err != nil {
		   return err
		}
	}
	return nil
}

func UpdateDistanceFee(m *DistanceFee, k ...string) (err error) {
       o := models.GetDefaultDB()
	v := DistanceFee{DistanceFeeId: m.DistanceFeeId}
	if err = o.Read(&v); err == nil {
		_, err = o.Update(m, k...)
		return err
	}
	return
}

func UpdateWeightFee(m *WeightFee, k ...string) (err error) {
       o := models.GetDefaultDB()
	v := WeightFee{WeightFeeId: m.WeightFeeId}
	if err = o.Read(&v); err == nil {
		_, err = o.Update(m, k...)
		return err
	}
	return
}
func UpdatePeriodFee(m *PeriodFee, k ...string) (err error) {
       o := models.GetDefaultDB()
	v := PeriodFee{PeriodFeeId: m.PeriodFeeId}
	if err = o.Read(&v); err == nil {
		_, err = o.Update(m, k...)
		return err
	}
	return
}

func DeleteFreightRule(id int64) (err error) {
	o := models.GetDefaultDB()
	v := FreightRule{FreightRuleId: id}
	if err = o.Read(&v); err == nil {
		if _, err = o.Delete(&v); err == nil {
			return nil
		}
	}
	return
}

func DeleteDistanceFee(id int64) (err error) {
	o := models.GetDefaultDB()
	v := DistanceFee{DistanceFeeId: id}
	if err = o.Read(&v); err == nil {
		if _, err = o.Delete(&v); err == nil {
			return nil
		}
	}
	return
}
func DeleteWeightFee(id int64) (err error) {
	o := models.GetDefaultDB()
	v := WeightFee{WeightFeeId: id}
	if err = o.Read(&v); err == nil {
		if _, err = o.Delete(&v); err == nil {
			return nil
		}
	}
	return
}
func DeletePeriodFee(id int64) (err error) {
	o := models.GetDefaultDB()
	v := PeriodFee{PeriodFeeId: id}
	if err = o.Read(&v); err == nil {
		if _, err = o.Delete(&v); err == nil {
			return nil
		}
	}
	return
}

func GetStepFreightFee(partnerId,storeId,stepExpressId int64,distance,weight float32,period time.Time) (float32, error){
	rule, err := GetFreightRuleById(stepExpressId)
	if err != nil {
		return 0.0, err
	}
	if len(rule.DistanceFees) < 1 {
	    return 0.0, errors.New("运费模板参数无效")
	}
	limitValue := rule.DistanceFees[len(rule.DistanceFees) - 1]
	if distance > limitValue.Upper {
	    return 0.0, errors.New("距离超出配送范围")
	}
	totalFee := rule.BaseFee
	if ruleJson, err := json.Marshal(rule); err == nil {
		beego.Debug("freight rule:", string(ruleJson))
	}
	beego.Debug("base_fee:", totalFee)
	//计算距离费用
	for _, value := range rule.DistanceFees {
	    var intervalDis float32 = 0.0
		var intervalFee float32 = 0.0
		if distance < value.Lower {
		    break
		}
		if distance >= value.Lower && distance <= value.Upper {
	        intervalDis = distance - value.Lower
		    if math.IsEqual(float64(value.Unit), 0.0) {
				intervalFee = value.UnitFee
			}else{
				intervalFee = intervalDis/value.Unit * value.UnitFee
			}
		    totalFee = totalFee + intervalFee
		    break
	    }
		intervalDis = value.Upper - value.Lower
		intervalFee = 0.0
		if math.IsEqual(float64(value.Unit), 0.0) {
		    intervalFee = value.UnitFee
		}else{
		    intervalFee = intervalDis/value.Unit * value.UnitFee
		}
		totalFee = totalFee + intervalFee
	}
	beego.Debug("add distance_fee:", totalFee)
	//计算重量费用
	for _, value := range rule.WeightFees {
	    var intervalDis float32 = 0.0
		var intervalFee float32 = 0.0
		if weight < value.Lower {
		    break
		}
		if weight >= value.Lower && weight <= value.Upper {
	        intervalDis = weight - value.Lower
		    if math.IsEqual(float64(value.Unit), 0.0) {
				intervalFee = value.UnitFee
			}else{
				intervalFee = intervalDis/value.Unit * value.UnitFee
			}
		    totalFee = totalFee + intervalFee
		    break
	    }
		intervalDis = value.Upper - value.Lower
		intervalFee = 0.0
		if math.IsEqual(float64(value.Unit), 0.0) {
		    intervalFee = value.UnitFee
		}else{
		    intervalFee = intervalDis/value.Unit * value.UnitFee
		}
		totalFee = totalFee + intervalFee
	}
	beego.Debug("add weight_fee:", totalFee)
	//计算时段费用
	curPeriod := period.Format("1504")
	curPeriodInt, err := strconv.Atoi(curPeriod)
	if err != nil {
	    return 0.0, err
	}
	field := "STORE_OPEN_TIME_" + strconv.FormatInt(storeId, 10)
	if value, err := misc.GetShopConfigByPartnerId(partnerId, field); err == nil {
	    periodPara := struct {
			Start string `json:"start"` 
			End   string `json:"end"` 
		}{}
		if err := json.Unmarshal([]byte(value), &periodPara); err == nil {
			if start, err := strconv.Atoi(periodPara.Start); err == nil {
			    if curPeriodInt < start {
			       return 0.0, errors.New("时间超出配送时段")
			    }
			}
			if end, err := strconv.Atoi(periodPara.End); err == nil {
			    if curPeriodInt > end {
			       return 0.0, errors.New("时间超出配送时段")
			    }
			}
		}
	}
	for _, value := range rule.PeriodFees {
		var intervalFee float32 = 0.0
		start := strings.Replace(value.Start, ":", "", -1)
		startInt, err := strconv.Atoi(start)
		if err != nil {
			return 0.0, err
		}
		end := strings.Replace(value.End, ":", "", -1)
	    endInt, err := strconv.Atoi(end)
		if err != nil {
			return 0.0, err
		}
		if curPeriodInt >= startInt && curPeriodInt <= endInt {
	        intervalFee = value.UnitFee
		    totalFee = totalFee + intervalFee
		    break
	    }
	}
	beego.Debug("add period_fee:", totalFee)
	return totalFee, nil
}
